class morphClass {
  int Xf;
  int Xt;
  int Xdif;
  int Yf;
  int Yt;
  int Ydif;
  morphClass() {
    Xf=0;Xt=0;Xdif= 0;Yf=0;Yt=0;Ydif=0;
  }
  morphClass(int X_f,int X_t,int X_dif,int Y_f,int Y_t,int Y_dif) {
    Xf = X_f;
    Xt = X_t;
    Xdif = X_dif;
    Yf = Y_f;
    Yt = Y_t;
    Ydif = Y_dif;
  }
  void setTo(int X_t,int Y_t) {
    Xt = X_t;
    Yt = Y_t;
  }
}

static int howMany=30;// number of clickable points

morphClass[] morphVar;
PImage[] img;
int[] p1X,p2X;
int[] p1Y,p2Y;

int pointToMove=-1;
int movePoint=0;
int imgShow=1;
int setPointNum=0;
int points1=0;
int points2=0;
int t=0;
int d=1;

boolean pointsShow=false;
boolean morphBool=false;
boolean morphFirst=true;
boolean deletePoint=false;
boolean pressedMouse=false;

void setup() {
  int i=0;
  size(500,500);
  background(0);
  PFont myFont = createFont("verdana", 9);
  textFont(myFont);
  img = new PImage[2];
  img[0] = loadImage("img2.jpg");
  img[1] = loadImage("img1.jpg");
  morphVar = new morphClass[howMany];
  p1X = new int[howMany];
  p2X = new int[howMany];
  p1Y = new int[howMany];
  p2Y = new int[howMany];
  for (i=0;i<howMany;i++) {
    p1X[i]=-1;
    p1Y[i]=-1;
    p2X[i]=-1;
    p2Y[i]=-1;
  }
  rectMode(CENTER);
  fill(255);
  stroke(0);
  //START SAMPLE NODES HERE
  points2 = 10;
  p2X[0] = 270; p2Y[0] = 469;
  p2X[1] = 349; p2Y[1] = 281;
  p2X[2] = 339; p2Y[2] = 278;
  p2X[3] = 331; p2Y[3] = 271;
  p2X[4] = 224; p2Y[4] = 265;
  p2X[5] = 211; p2Y[5] = 267;
  p2X[6] = 201; p2Y[6] = 272;
  p2X[7] = 271; p2Y[7] = 105;
  p2X[8] = 170; p2Y[8] = 122;
  p2X[9] = 351; p2Y[9] = 111;

  points1 = 10;
  p1X[0] = 304; p1Y[0] = 425;
  p1X[1] = 410; p1Y[1] = 217;
  p1X[2] = 404; p1Y[2] = 212;
  p1X[3] = 412; p1Y[3] = 217;
  p1X[4] = 208; p1Y[4] = 197;
  p1X[5] = 185; p1Y[5] = 207;
  p1X[6] = 193; p1Y[6] = 198;
  p1X[7] = 264; p1Y[7] = 25;
  p1X[8] = 37;  p1Y[8] = 39;
  p1X[9] = 449; p1Y[9] = 28;
  
  setPointNum = 10;
  morphBool=true;
  morphFirst=true;
  pointsShow=true;
}

void pointDelete() {
  int i=0,k=0,distance=0,minDist=height*width;
  minDist=height*width;
  if (imgShow==0) {
    for (i=0;i<points1;i++) {
        distance = (p1X[i]-mouseX)*(p1X[i]-mouseX) + (p1Y[i]-mouseY)*(p1Y[i]-mouseY);
        if (distance<minDist) { minDist=distance; k=i; }
    }  
    p1X[k]=-1;
    p1Y[k]=-1;
    points1--;
  }
  else if (imgShow==1) {
    for (i=0;i<points2;i++) {
        distance = (p2X[i]-mouseX)*(p2X[i]-mouseX) + (p2Y[i]-mouseY)*(p2Y[i]-mouseY);
        if (distance<minDist) { minDist=distance; k=i; }
    }  
    p2X[k]=-1;
    p2Y[k]=-1;
    points2--;
  }
}

void pointMove() {
  int i=0,k=0,distance=0,minDist=height*width;
  println(mouseX+"  "+mouseY);
  if (pointToMove>=0) {
    if (imgShow==0) {
        p1X[pointToMove]=mouseX;
        p1Y[pointToMove]=mouseY;
    }
    else if (imgShow==1) {
        p2X[pointToMove]=mouseX;
        p2Y[pointToMove]=mouseY;
    }
    return;
  } 
  minDist=height*width;
  if (imgShow==0) {
    for (i=0;i<points1;i++) {
        distance = (p1X[i]-mouseX)*(p1X[i]-mouseX) + (p1Y[i]-mouseY)*(p1Y[i]-mouseY);
        if (distance<minDist) { minDist=distance; k=i; }
    }  
    pointToMove=k;
  }
  else if (imgShow==1) {
    for (i=0;i<points2;i++) {
        distance = (p2X[i]-mouseX)*(p2X[i]-mouseX) + (p2Y[i]-mouseY)*(p2Y[i]-mouseY);
        if (distance<minDist) { minDist=distance; k=i; }
    }  
    pointToMove = k;
  }
}

void morphStep6(morphClass[] m,int t) {
  //we know that t is a number between 0 and 100
  int i=0,j=0,k=0;
  int in1=0,jn1=0,in2=0,jn2=0;
  int[] Xf2,Xt2,Yf2,Yt2;
  float[] A1,B1,C1,D1,A2,B2,C2,D2;
  float r=0,g=0,b=0,cx=0;
  int difx1=0,difx2=0,dify1=0,dify2=0;
  int divide=1;
  
  Xf2 = new int[howMany];
  Xt2 = new int[howMany];
  Yf2 = new int[howMany];
  Yt2 = new int[howMany];
  
  A1 = new float[howMany];
  B1 = new float[howMany];
  C1 = new float[howMany];
  D1 = new float[howMany];
  A2 = new float[howMany];
  B2 = new float[howMany];
  C2 = new float[howMany];
  D2 = new float[howMany];

  for (i=0;i<setPointNum;i++) {
    Xf2[i] = m[i].Xf + int(t*m[i].Xdif*0.01);
    Yf2[i] = m[i].Yf + int(t*m[i].Ydif*0.01);
    Xt2[i] = m[i].Xf + m[i].Xdif;
    Yt2[i] = m[i].Yf + m[i].Ydif;

    A1[i] = float(m[i].Xf)/(m[i].Xt);
    B1[i] = float(width-m[i].Xf)/(width-m[i].Xt);
    C1[i] = float(m[i].Yf)/(m[i].Yt);
    D1[i] = float(height-m[i].Yf)/(height-m[i].Yt);

    A2[i] = float(Xt2[i])/(Xf2[i]);
    B2[i] = float(width-Xt2[i])/(width-Xf2[i]);
    C2[i] = float(Yt2[i])/(Yf2[i]);
    D2[i] = float(height-Yt2[i])/(height-Yf2[i]);
  }
  
  loadPixels();  
  for (i=0;i<width;i++) 
    for (j=0;j<height;j++) {
      difx1=0;difx2=0;dify1=0;dify2=0;
      divide=0;
      for (k=0;k<setPointNum;k++) {
         if (i<m[k].Xt)   difx1 += int(i*A1[k]) - i;                       /*A1x*/
         else             difx1 += m[k].Xf + int((i-m[k].Xt)*B1[k]) - i;   /*B1x*/
         if (j<m[k].Yt)   dify1 += int(j*C1[k]) - j;                       /*C1y*/
         else             dify1 += m[k].Yf + int((j-m[k].Yt)*D1[k]) - j;   /*D1y*/

         if (i<Xf2[k])   difx2 += int(i*A2[k]) - i;                       /*A2x*/
         else            difx2 += Xt2[k] + int((i-Xf2[k])*B2[k]) - i;     /*B2x*/
         if (j<Yf2[k])   dify2 += int(j*C2[k]) - j;                       /*C2y*/
         else            dify2 += Yt2[k] + int((j-Yf2[k])*D2[k]) - j;     /*D2y*/ 
         divide++;
      }//endof k
      in1 = int(i + (float)difx1/divide);
      in2 = int(i + (float)difx2/divide);
      jn1 = int(j + (float)dify1/divide);
      jn2 = int(j + (float)dify2/divide);
      r = (float)(red(img[0].pixels[in1+jn1*width])*(100-t) + red(img[1].pixels[in2+jn2*width])*(t))/100;
      g = (float)(green(img[0].pixels[in1+jn1*width])*(100-t) + green(img[1].pixels[in2+jn2*width])*(t) )/100;
      b = (float)(blue(img[0].pixels[in1+jn1*width])*(100-t) + blue(img[1].pixels[in2+jn2*width])*(t))/100;
      pixels[i+j*width] = color(r,g,b);
    }//end of for j
  updatePixels();
  if (pointsShow) for (k=0;k<setPointNum;k++)  {
    rect(m[k].Xt,m[k].Yt,5,5);
    text(k, m[k].Xt+3,m[k].Yt+3);
  }
  return;
}

void setMorphs(morphClass[] m) {
  int i=0,j=0,k=0;
  for (j=0;j<setPointNum;j++)
    morphVar[j] = new morphClass(p1X[j],p1X[j],p2X[j]-p1X[j],p1Y[j],p1Y[j],p2Y[j]-p1Y[j]);
  return;
}

void updateMorphParam(morphClass[] m,int t) {
  int j=0;
  for (j=0;j<setPointNum;j++)
    m[j].setTo(int(m[j].Xf+m[j].Xdif*(float)t/100),int(m[j].Yf+m[j].Ydif*(float)t/100));
  return;
}

void morphDraw() {
  int i=0;
  if (morphBool&&morphFirst) {
    setMorphs(morphVar);
    morphFirst=false;
  }
  else updateMorphParam(morphVar,t);
  t+=d;
  if ((d==1)&&(t>99)) d=-1;
  if ((d==-1)&&(t<1)) d=1;
  morphStep6(morphVar,t);
  return;
}

void draw() {
  int i=0;
  if (movePoint==2) pointMove();
  if ((morphBool)&&(setPointNum>0)) morphDraw();
  else {
    image(img[imgShow],0,0,width,height);
    for (i=0;i<howMany;i++) {
      stroke(255,255,255);
      if ((imgShow==0)&&(p1X[i]>0)&&(p1Y[i]>0)) {
        rect(p1X[i],p1Y[i],5,5); 
        text(i, p1X[i]+3,p1Y[i]+3);
      }
      if ((imgShow==1)&&(p2X[i]>0)&&(p2Y[i]>0)){
         rect(p2X[i],p2Y[i],5,5); 
         text(i, p2X[i]+3,p2Y[i]+3);
      }
    }
  }
}

void matchPoints() {
  int[] p1Xbuf,p1Ybuf,p2Xbuf,p2Ybuf,unMatched;
  int i=0,j=0,k=0,distance=0,minDist=height*width;

  p1Xbuf = new int[setPointNum];
  p1Ybuf = new int[setPointNum];
  p2Xbuf = new int[setPointNum];
  p2Ybuf = new int[setPointNum];
  unMatched = new int[max(points1,points2)];
  
  for (i=0;i<max(points1,points2);i++) unMatched[i]=1;
  
  if (points1<=points2) {//unmatched points will of second picture
    for (i=0;i<setPointNum;i++) {
      minDist=height*width;
      for (j=0;j<setPointNum;j++) {
          distance = (p1X[i]-p2X[j])*(p1X[i]-p2X[j]) + (p1Y[i]-p2Y[j])*(p1Y[i]-p2Y[j]);
          if (distance<minDist) { minDist=distance; k=j; unMatched[k]=0;}
      }  
      p1Xbuf[i]=p1X[i]; p1X[i]=2*width;
      p1Ybuf[i]=p1Y[i]; p1Y[i]=2*height;
      p2Xbuf[i]=p2X[k]; p2X[k]=2*width;
      p2Ybuf[i]=p2Y[k]; p2Y[k]=2*height;
    }
    j=setPointNum;
    if (points2!=setPointNum)
      for (i=0;i<points2;i++) 
        if (unMatched[i]==1) {
          p2X[j] = p2X[i];
          p2Y[j] = p2Y[i];
          j++;
        }
  }

  else if (points2<points1) {//unmatched points will of first picture
    for (i=0;i<setPointNum;i++) {
      minDist=height*width;
      for (j=0;j<setPointNum;j++) {
          distance = (p1X[i]-p2X[j])*(p1X[i]-p2X[j]) + (p1Y[i]-p2Y[j])*(p1Y[i]-p2Y[j]);
          if (distance<minDist) { minDist=distance; k=j; unMatched[k]=0;}
      }  
      p2Xbuf[i]=p2X[i]; p2X[i]=2*width;
      p2Ybuf[i]=p2Y[i]; p2Y[i]=2*height;
      p1Xbuf[i]=p1X[k]; p1X[k]=2*width;
      p1Ybuf[i]=p1Y[k]; p1Y[k]=2*height;
    }
    j=setPointNum;
    if (points1!=setPointNum)
      for (i=0;i<points1;i++) 
        if (unMatched[i]==1) {
          p1X[j] = p1X[i];
          p1Y[j] = p1Y[i];
          j++;
        }
  }

  for (i=0;i<setPointNum;i++) {
    p1X[i]=p1Xbuf[i];
    p1Y[i]=p1Ybuf[i];
    p2X[i]=p2Xbuf[i];
    p2Y[i]=p2Ybuf[i];
  }
}

void keyPressed() {
  int i=0;
  if ((key=='p')||(key=='P')) pointsShow = !pointsShow;
  if ((key=='d')||(key=='D')) deletePoint =true; else deletePoint=false;
  if ((key=='t')||(key=='T')) movePoint = (movePoint+1)%2; else movePoint=0;
  if ((key=='s')||(key=='S')) imgShow=(imgShow+1)%2;
  if (((key=='m')||(key=='M'))&&(setPointNum>0)) {
    matchPoints();
    morphBool = !morphBool;
    if (!morphBool) morphFirst=true; 
  }
  if ((key=='r')||(key=='R')) {
    for (i=0;i<howMany;i++) {
      p1X[i]=-1;
      p1Y[i]=-1;
      p2X[i]=-1;
      p2Y[i]=-1;
    }
    pointsShow = true;
    morphBool = false;
    morphFirst=true;
    deletePoint = false;
    movePoint = 0;
    setPointNum=0;
    points1=0;
    points2=0;
  }
}

void mousePressed() {
  if (morphBool) return;
  if (deletePoint) {
    pointDelete();
    setPointNum = min(points1,points2);
    return;
  }
  if (movePoint==1) {
    movePoint=2;
    return;
  }
  if (imgShow==0) {
    p1X[points1] = mouseX;
    p1Y[points1] = mouseY;
    points1++;
  }
  else {
    p2X[points2] = mouseX;
    p2Y[points2] = mouseY;
    points2++;
  }
  setPointNum = min(points1,points2);
}

void mouseReleased() {
  pressedMouse=false;
  println(movePoint);
  if (movePoint==2) {
    movePoint=1;
    pointToMove=-1;
    return;
  }
}
